#ifndef ILLIB_LOG_H_
#define ILLIB_LOG_H_

#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>

#define AT() __FILE__, __LINE__
#define EX(x) (x), #x
#define EXP(x) static_cast<void*>(x), #x
#define EXR(x) static_cast<void*>(&x), #x
#define TN(x) #x
#define SZ(x) sizeof(x), #x


namespace illib {
	namespace Console {
		void print(const char *fmt, ...);
		void vaprint(const char *fmt, va_list args);
	}
	class ConsoleLogger{
	private:
		ConsoleLogger(const ConsoleLogger &other);
		ConsoleLogger();
	protected:
		uint16_t indent_;
		ConsoleLogger * parent_;
		const char * delimeter_;
		const char * indenter_;
	public:
		enum Format {
			Hex,
			Decimal
		};
		ConsoleLogger(ConsoleLogger * parent, const char * delimeter = 0, const char * indent = 0) :
			indent_(0),
			parent_(parent),
			delimeter_(delimeter),
			indenter_(indent)
		{
			if(parent_ && !delimeter) delimeter_ = parent_->delimeter_;
			if(parent_ && !indent) indenter_ = parent_->indenter_;
			if(parent_) indent_ = parent_->indent_ + 1;
		}
		~ConsoleLogger()
		{
		}
		void indent(int8_t offset = 0){
			for(uint16_t i = 0; i < indent_ + offset; i++) Console::print(indenter_);
		}
		void pointer(void * value, const char *name){
			indent();
			Console::print("%s: %p%s", name, value, delimeter_);
		}
		void reference(void * value, const char *name){
			indent();
			Console::print("%s: %p%s", name, value, delimeter_);
		}
		void boolean(bool value, const char *name){
			indent();
			Console::print("%s: %s%s", name, (value) ? "true" : "false", delimeter_);
		}
		void string(const char * value, const char *name){
			indent();
			Console::print("%s: '%s'%s", name, value, delimeter_);
		}
		void number(uint64_t value, const char *name,  Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(uint32_t value,const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(uint16_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(uint8_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(int64_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(int32_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(int16_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
		void number(int8_t value, const char *name, Format fmt = Decimal){
			indent();
			if(fmt == Decimal) Console::print("%s: %d%s", name, value, delimeter_);
			else if(fmt == Hex) Console::print("%s: %x%s", name, value, delimeter_);
		}
	};
	class ObjectLogger : public ConsoleLogger{
		void * obj_;
		unsigned int size_;
		const char * name_;
	public:
		ObjectLogger(ConsoleLogger * parent, unsigned int size, const char * name, void * obj, const char * delimeter = 0, const char * indent = 0) :
			ConsoleLogger(parent, delimeter, indent),
			obj_(obj),
			size_(size),
			name_(name)
		{
			ConsoleLogger::indent(-1);
			Console::print("%s (%X) {%s", name, obj, delimeter_);
		}
		~ObjectLogger()
		{
			indent(-1);
			Console::print("}%s", delimeter_);
		}
	};
}

#endif /* ILLIB_LOG_H_ */
